package org.wang.generalcode.modules.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.wang.generalcode.modules.sys.constant.SystemConstants;
import org.wang.generalcode.modules.sys.converter.MenuConverter;
import org.wang.generalcode.modules.sys.mapper.MenuMapper;
import org.wang.generalcode.modules.sys.pojo.entity.Menu;
import org.wang.generalcode.modules.sys.pojo.enums.MenuTypeEnum;
import org.wang.generalcode.modules.sys.pojo.enums.StatusEnum;
import org.wang.generalcode.modules.sys.pojo.model.KeyValue;
import org.wang.generalcode.modules.sys.pojo.model.Option;
import org.wang.generalcode.modules.sys.pojo.params.MenuParams;
import org.wang.generalcode.modules.sys.pojo.params.PageParams;
import org.wang.generalcode.modules.sys.pojo.vo.MenuVO;
import org.wang.generalcode.modules.sys.pojo.vo.MetaVO;
import org.wang.generalcode.modules.sys.pojo.vo.RouteVO;
import org.wang.generalcode.modules.sys.service.IMenuService;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单管理 服务实现类
 *
 * @author wz
 * @since 2024-08-08
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {

    @Resource
    private MenuConverter menuConverter;

    @Override
    public List<MenuVO> listMenus(PageParams menu) {
        List<Menu> menus = baseMapper.listMenus(menu);
//        List<Menu> menus = this.list(new LambdaQueryWrapper<Menu>()
//                .like(StrUtil.isNotBlank(menu.getKeywords()), Menu::getName, menu.getKeywords())
//                .orderByAsc(Menu::getSort)
//        );
        // 获取所有菜单ID
        Set<Long> menuIds = menus.stream()
                .map(Menu::getId)
                .collect(Collectors.toSet());

        // 获取所有父级ID
        Set<Long> parentIds = menus.stream()
                .map(Menu::getParentId)
                .collect(Collectors.toSet());

        // 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
        List<Long> rootIds = parentIds.stream()
                .filter(id -> !menuIds.contains(id))
                .toList();

        // 使用递归函数来构建菜单树
        return rootIds.stream()
                .flatMap(rootId -> buildMenuTree(rootId, menus).stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<RouteVO> listRoutes(List<String> roles) {
        if (CollectionUtil.isEmpty(roles)) {
            return Collections.emptyList();
        }

        List<Menu> menuList = this.baseMapper.listRoutes(roles);
        return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList);
    }

    @Override
    public List<Option> listMenuOptions(boolean onlyParent) {

        List<Menu> menuList = this.list(new LambdaQueryWrapper<Menu>()
                .in(onlyParent, Menu::getType, MenuTypeEnum.CATALOG.getValue(), MenuTypeEnum.MENU.getValue())
                .orderByAsc(Menu::getSort)
        );
        return buildMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
    }

    @Override
    public boolean saveMenu(MenuParams menuParams) {
        MenuTypeEnum menuType = menuParams.getType();

        if (menuType == MenuTypeEnum.CATALOG) {  // 如果是外链
            String path = menuParams.getRoutePath();
            if (menuParams.getParentId() == 0 && !path.startsWith("/")) {
                menuParams.setRoutePath("/" + path); // 一级目录需以 / 开头
            }
            menuParams.setComponent("Layout");
        } else if (menuType == MenuTypeEnum.EXTLINK) {   // 如果是目录

            menuParams.setComponent(null);
        }

        Menu entity = menuConverter.toEntity(menuParams);
        String treePath = generateMenuTreePath(menuParams.getParentId());
        entity.setTreePath(treePath);

        List<KeyValue> params = menuParams.getParams();
        // 路由参数 [{key:"id",value:"1"}，{key:"name",value:"张三"}] 转换为 [{"id":"1"},{"name":"张三"}]
        if (CollectionUtil.isNotEmpty(params)) {
            entity.setParams(JSONUtil.toJsonStr(params.stream()
                    .collect(Collectors.toMap(KeyValue::getKey, KeyValue::getValue))));
        } else {
            entity.setParams(null);
        }
        if (menuType != MenuTypeEnum.BUTTON) {
            Assert.isFalse(this.exists(new LambdaQueryWrapper<Menu>()
                    .eq(Menu::getRouteName, entity.getRouteName())
                    .ne(menuParams.getId() != null, Menu::getId, menuParams.getId())
            ), "路由名称已存在");
        }

        boolean result = this.saveOrUpdate(entity);
//        if (result) {
//            // 编辑刷新角色权限缓存
//            if (menuParams.getId() != null) {
//                roleMenuService.refreshRolePermsCache();
//            }
//        }
        return result;
    }

    /**
     * 部门路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    private String generateMenuTreePath(Long parentId) {
        if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
            return String.valueOf(parentId);
        } else {
            Menu parent = this.getById(parentId);
            return parent != null ? parent.getTreePath() + "," + parent.getId() : null;
        }
    }


    /**
     * 递归生成菜单下拉层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单下拉列表
     */
    private List<Option> buildMenuOptions(Long parentId, List<Menu> menuList) {
        List<Option> menuOptions = new ArrayList<>();

        for (Menu menu : menuList) {
            if (menu.getParentId().equals(parentId)) {
                Option option = new Option(menu.getId(), menu.getName());
                List<Option> subMenuOptions = buildMenuOptions(menu.getId(), menuList);
                if (!subMenuOptions.isEmpty()) {
                    option.setChildren(subMenuOptions);
                }
                menuOptions.add(option);
            }
        }

        return menuOptions;
    }


    /**
     * 递归生成菜单列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单列表
     */
    private List<MenuVO> buildMenuTree(Long parentId, List<Menu> menuList) {
        return CollectionUtil.emptyIfNull(menuList)
                .stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .map(entity -> {
                    MenuVO menuVO = BeanUtil.copyProperties(entity, MenuVO.class);
                    List<MenuVO> children = buildMenuTree(entity.getId(), menuList);
                    menuVO.setChildren(children);
                    return menuVO;
                }).toList();
    }

    /**
     * 递归生成菜单路由层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 路由层级列表
     */
    private List<RouteVO> buildRoutes(Long parentId, List<Menu> menuList) {
        List<RouteVO> routeList = new ArrayList<>();

        for (Menu menuBo : menuList) {
            if (menuBo.getParentId().equals(parentId)) {
                RouteVO routeVO = toMenuVO(menuBo);
                List<RouteVO> children = buildRoutes(menuBo.getId(), menuList);
                if (!children.isEmpty()) {
                    routeVO.setChildren(children);
                }
                routeList.add(routeVO);
            }
        }

        return routeList;
    }


    /**
     * 根据RouteBO创建RouteVO
     */
    private RouteVO toMenuVO(Menu routeBO) {
        RouteVO routeVO = new RouteVO();
        // 获取路由名称
        String routeName = routeBO.getRouteName();
        if (StrUtil.isBlank(routeName)) {
            // 路由 name 需要驼峰，首字母大写
            routeName = StringUtils.capitalize(StrUtil.toCamelCase(routeBO.getRoutePath(), '-'));
        }
        // 根据name路由跳转 this.$router.push({name:xxx})
        routeVO.setName(routeName);

        // 根据path路由跳转 this.$router.push({path:xxx})
        routeVO.setPath(routeBO.getRoutePath());
        routeVO.setRedirect(routeBO.getRedirect());
        routeVO.setComponent(routeBO.getComponent());

        MetaVO meta = new MetaVO();
        meta.setTitle(routeBO.getName());
        meta.setIcon(routeBO.getIcon());
        meta.setHidden(StatusEnum.DISABLE.getValue().equals(routeBO.getVisible()));
        // 【菜单】是否开启页面缓存
        if (MenuTypeEnum.MENU.equals(routeBO.getType())
                && ObjectUtil.equals(routeBO.getKeepAlive(), 1)) {
            meta.setKeepAlive(true);
        }
        meta.setAlwaysShow(ObjectUtil.equals(routeBO.getAlwaysShow(), 1));

        String paramsJson = routeBO.getParams();
        // 将 JSON 字符串转换为 Map<String, String>
        if (StrUtil.isNotBlank(paramsJson)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                Map<String, String> paramMap = objectMapper.readValue(paramsJson, new TypeReference<>() {
                });
                meta.setParams(paramMap);
            } catch (Exception e) {
                throw new RuntimeException("解析参数失败", e);
            }
        }
        routeVO.setMeta(meta);
        return routeVO;
    }

}
